<?php
//PEARパッケージの読込
ini_set('include_path', '/usr/local/lib/php' . PATH_SEPARATOR . ini_get('include_path'));
require_once('DB.php');
//バリデータ
require_once('Validator.php');
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
 * Model 
 * 
 * @package 
 * @version $id$
 * @author kamisiro.memo <kamisiro.memo@gmail.com> 
 */
class Model extends Validator
{
    /**
     * [con]
     *
     * データベースコネクション
     *
     * @var    $con
     * @access public
     */
    var $con    = null;

    /**
     * [dsn]
     *
     * DB接続用文字列
     *
     * @var    $dsn
     * @access public
     */
    var $dsn    = '';

    /**
     * [useTable] 
     *
     * モデルで使用するテーブル名
     * 
     * @var string
     * @access public
     */
    var $useTable = "";

    /**
     * クエリのログを吐き出すかの設定
     * TRUEであればクエリログを作成する
     *
     * @const  QUERY_LOG
     * @access public
     */
    const   QUERY_LOG = TRUE;

    /**
     * クエリのログのパス
     *
     * @const  LOG_PATH
     * @access public
     */
    const   LOG_PATH = '/var/www/html/tools/app/logs/query.log';

    /**
     * [startDb]
     *
     * DB接続を開始する
     *
     * @return  none
     * @access  public
     */
    public function startDb()
    {
        $this->con =& DB::connect($this->dsn);

        if (PEAR::isError($this->con)) {
            die($this->con->getMessage());
        }
    }

    /**
     * [closeDb]
     *
     * DB接続を終了する
     *
     * @return  none
     * @access  public
     */
    public function closeDb()
    {
        if ($this->con !== null) {
            $this->con->disconnect();
        }
    }

    /**
     * [findQuery]
     * Query
     * このメソッドを使う際 クエリのパラメータは検査済みという前提
     *
     * @param   string  $query  クエリ
     * @return  mixed
     * @access  public
     */
    public function findQuery($query = null)
    {
        if ($query === null) {
            return false; 
        }
        
        $this->_chkDbConnect();

        //ログを取得するかどうかの設定
        if (self::QUERY_LOG) {
            error_log('['.date('Y/m/d H:i:s').']Query: '. $query . "\n", 3, self::LOG_PATH);
        }

        $res =& $this->con->query($query);

        if (PEAR::isError($res)) {
            die($res->getMessage());
        }

        while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            $ret[] = $row;
        }

        return $ret;
    }

    /**
     * [execute] 
     * 
     * @param mixed $query 
     * @access public
     * @return void
     */
    public function execute($query = null)
    {

        if ($query === null) {
            return false; 
        }
        
        $this->_chkDbConnect();

        $res =& $this->con->query($query);

        if (PEAR::isError($res)) {
            return $res->getMessage();
        }
        
        return true; 
    }

    /**
     * [findOne]
     *
     * SELECT 単体
     * 先頭の一行のみを返却する
     *
     * @param   array   $cond   WHERE条件 必須ではない
     * @param   array   $order  ソート条件 必須ではない
     * @return  mixed
     * @access  public
     */
    public function findOne($cond = null, $order = null)
    {
        $res = $this->_find($this->useTable, $cond, $order);
        
        $ret = NULL;
        while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            $ret[] = $row;
        }
        
        if ($ret === NULL) {
            return false; 
        }

        return $ret[0];
    }

    /**
     * [findList]
     *
     * SELECT List 
     * 全ての行を返却する
     *
     * @param   array   $cond   WHERE条件 必須ではない
     * @param   array   $order  ソート条件 必須ではない
     * @return  mixed
     * @access  public
     */
    public function findList($cond = null, $order = null)
    {
        $res = $this->_find($this->useTable, $cond, $order);
        
        $ret = NULL;
        while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            $ret[] = $row;
        }

        if ($ret === NULL) {
            return false; 
        }
        
        return $ret;
    }

    /**
     * [_find]
     *
     * 検索の基底メソッド
     *
     * @param   string  $table  テーブル名
     * @param   array   $cond   WHERE条件 必須ではない
     * @param   array   $order  ソート条件 必須ではない
     * @return  mixed 
     * @access  private
     */
    private function _find($table, $cond = null, $order = null)
    {
        $this->_chkDbConnect();

        //条件がある場合
        $condition = null;
        $tmp_cond  = array();
        if ($cond !== null && is_array($cond)) {
            foreach ($cond as $field => $val)  {
                $tmp_cond[] = $this->con->escapeSimple($field) . " = " . $this->con->quoteSmart($val) . "";
            }
            $condition = "WHERE ";
            $condition .= join(' AND ', $tmp_cond);
        }

        //OrderBy
        if ($order !== null) {
            $order = ' ORDER BY ' . $order; 
        }

        //SELECT文
        $query = sprintf("
            SELECT * FROM %s %s %s
        ",  $this->con->escapeSimple($table),
            $condition,
            $order);

        //ログを取得するかどうかの設定
        if (self::QUERY_LOG) {
            error_log('['.date('Y/m/d H:i:s').']Query: '. $query . "\n", 3, self::LOG_PATH);
        }

        $res =& $this->con->query($query);

        if (PEAR::isError($res)) {
            die($res->getMessage());
        }

        return $res;
    }

    /**
     * [saveOne]
     *
     * INSERT処理を行う
     *
     * @param   array   $params 保存を行うパラメータ
     * @return  mixed
     * @access  public
     */
    public function saveOne($params = null)
    {
        if (!is_array($params)) {
            return false; 
        }
        
        $this->_chkDbConnect();

        $column = array();
        $value  = array();

        foreach ($params as $field => $val) {
            $column[] = $this->con->escapeSimple($field);
            $value[]  = $this->con->quoteSmart($val);
        }

        $query = sprintf("
            INSERT INTO `%s` (
                %s 
            ) VALUES (
                %s 
            );
        ",  $this->con->escapeSimple($this->useTable),
            join(',', $column),
            join(',', $value));

        //ログを取得するかどうかの設定
        if (self::QUERY_LOG) {
            error_log('['.date('Y/m/d H:i:s').']Query: '. $query . "\n", 3, self::LOG_PATH);
        }
        $res =& $this->con->query($query);

        if (PEAR::isError($res)) {
            die($res->getMessage());
        }

        return $res;
    }

    /**
     * [updateOne]
     *
     * UPDATE処理を行う
     *
     * @param   array   $params アップデートを行うパラメータ
     * @param   array   $cond   WHERE条件 必須ではない
     * @return  mixed
     * @access  public
     */
    public function updateOne($params, $cond = null)
    {
        if (!is_array($params)) {
            return false; 
        }

        $this->_chkDbConnect();
        
        $update = array();
        foreach ($params as $field => $val) {
            $update[] = $this->con->escapeSimple($field) . " = " . $this->con->quoteSmart($val);
        }

        //条件がある場合
        $condition = null;
        $tmp_cond  = array();
        if ($cond !== null && is_array($cond)) {
            foreach ($cond as $field => $val)  {
                $tmp_cond[] = $this->con->escapeSimple($field) . " = " . $this->con->quoteSmart($val) . "";
            }
            $condition = "WHERE ";
            $condition .= join(' AND ', $tmp_cond);
        }

        $query = sprintf("
            UPDATE `%s` SET
                %s 
                %s
        ",  $this->con->escapeSimple($this->useTable),
            join(',', $update),
            $condition);

        //ログを取得するかどうかの設定
        if (self::QUERY_LOG) {
            error_log('['.date('Y/m/d H:i:s').']Query: '. $query . "\n", 3, self::LOG_PATH);
        }

        $res =& $this->con->query($query);

        if (PEAR::isError($res)) {
            die($res->getMessage());
        }

        return $res;
    }

    /**
     * [deleteOne]
     *
     * DELETE処理
     *
     * @param   array   $cond   WHERE条件 必須ではない
     * @return  mixed
     * @access  public
     */
    public function deleteOne($cond = null)
    {
        $this->_chkDbConnect();

        //条件がある場合
        $condition = null;
        $tmp_cond  = array();
        if ($cond !== null && is_array($cond)) {
            foreach ($cond as $field => $val)  {
                $tmp_cond[] = $this->con->escapeSimple($field) . " = " . $this->con->quoteSmart($val) . "";
            }
            $condition = "WHERE ";
            $condition .= join(' AND ', $tmp_cond);
        }

        $query = sprintf("
            DELETE FROM `%s` %s
        ",  $this->con->escapeSimple($this->useTable),
            $condition);

        //ログを取得するかどうかの設定
        if(self::QUERY_LOG) {
            error_log('['.date('Y/m/d H:i:s').']Query: '. $query . "\n", 3, self::LOG_PATH);
        }

        $res =& $this->con->query($query);

        if (PEAR::isError($res)) {
            die($res->getMessage());
        }

        return $res;
    }

    /**
     * [_chkDbConnect]
     *
     * データベースとのコネクションが張られているかをチェックする
     * 張られていない場合は、接続を行う
     *
     * @return  none
     * @access  private
     */
    private function _chkDbConnect()
    {
        if($this->con === null) {
            $this->startDb();
        }
    }

    /**
     * [createTable] 
     *
     * app/sqlディレクトリ配下にあるcreate_xxxxといったファイルを見つけ出してテーブルを作成する
     * 
     * @access public
     * @return void
     */
    public function createTable()
    {
        $file_path = SQL_ROOT_PATH . DS . 'create_' . $this->useTable . '.sql';
        if (file_exists($file_path)) {
            $query = file_get_contents($file_path);
            if ($this->execute($query)) {
                return true; 
            }
        } else {
            trigger_error('No Such Query File : ' . $file_path);
            return false;
        }
    }

    /**
     * [getLastInsertID]
     *
     * 最後にインサートしたIDを取得
     *
     * @access public
     * @return int
     */
    public function getLastInsertID()
    {
        $this->_chkDbConnect();

        $query = sprintf("
            SELECT MAX(id) as max_id FROM %s
        ",  $this->con->escapeSimple($this->useTable));

        //ログを取得するかどうかの設定
        if (self::QUERY_LOG) {
            error_log('['.date('Y/m/d H:i:s').']Query: '. $query . "\n", 3, self::LOG_PATH);
        }

        $res =& $this->con->query($query);

        if (PEAR::isError($res)) {
            die($res->getMessage());
        }

        while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) {
            $ret[] = $row;
        }

        if ($ret === NULL) {
            return false; 
        }

        return $ret[0]['max_id'];
   
    }
}
